#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>
#include <queue>


using namespace std;

// 线程池线程个数
static const int defaultNum = 5;

struct ThreadData
{
    string name;
    pthread_t tid;
};

template <class T>
class ThreadPool
{
public:


    // 类内封装的接口
    string getThreadName(pthread_t tid)
    {
        for (auto &t : _threads)
        {
            if (tid == t.tid)
            {
                return t.name;
            }
        }
        return "None";
    }

    //加锁
    void Lock()
    {
        pthread_mutex_lock(&_mutex);
    }

    //解锁
    void UnLock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    //让线程在条件变量下等待
    void ToSleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    //唤醒在条件变量下等待的线程
    void WakeUp()
    {
        pthread_cond_signal(&_cond);
    }

    //判断是否有任务
    bool taskIsEmpty()
    {
        return _tasks.empty();
    }

public:


    // 为什么使用static
    // 因为linux原生线程库规定了线程处理函数的返回值必须是void*,参数是void*
    // 如果不加static,成员函数中是隐藏了一个 this指针,参数类型就会不一致
    // 同时也因为加了static，函数中不能访问普通成员和普通函数，必须用传过来的this指针调用
    static void *RunTask(void *args)
    {
        ThreadPool<T> *tp = static_cast<ThreadPool<T>*>(args);
        string name = tp->getThreadName(pthread_self());
        while (1)
        {
            // 加锁
            tp->Lock();
            // 无任务则等待,while循环防止伪唤醒
            while (tp->taskIsEmpty())
            {
                tp->ToSleep();
            }
            // 获取任务
            T t = tp->pop();
            tp->UnLock();

            t.run();
            cout << name << "run, result: " << t.GetResult() << endl;
        }
    }

    void push(const T &t)
    {
        //任务队列为临界资源，访问需要加锁
        Lock();
        _tasks.push(t);
        //有了任务之后要唤醒在条件变量下等待的线程
        WakeUp();
        UnLock();
    }

    // 输出型参数
    // 因为pop已经在加锁之内，所以不需要再加锁了
    T pop()
    {
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }

    // 创建线程
    void start()
    {
        int num = _threads.size();
        for (int i = 0; i < num; i++)
        {
            _threads[i].name = "thread-" + to_string(i + 1);
            pthread_create(&(_threads[i].tid), nullptr, RunTask, this);
        }
    }

    static ThreadPool<T>* GetInstance()
    {
        if(_tp == nullptr)
        {
            pthread_mutex_lock(&_lock);
            if(_tp == nullptr)
            {
                cout << "log: singleton create done first!" << endl;
                _tp = new ThreadPool<T>();
            }
            pthread_mutex_unlock(&_lock);
        }
        return _tp;
    }

private:
    ThreadPool(int num = defaultNum)
        :_threads(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    //拷贝构造和赋值重载设为delete
    ThreadPool(const ThreadPool<T>& tp) = delete;
    ThreadPool<T> operator=(const ThreadPool<T>& tp) = delete;

private:
    vector<ThreadData> _threads;
    queue<T> _tasks;

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> * _tp;
    static pthread_mutex_t _lock;
};

template<class T> 
ThreadPool<T> * ThreadPool<T>::_tp;

template<class T> 
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;